#include "cfg.h"

#include <stdio.h>
#include <string.h>

#include "cmsis_os.h"
#include "lwip.h"
#include "spi.h"
#include "tcp_com.h"

dev_cfg_t cfg = {0};
dev_cfg_t new_cfg;

void restore_cfg(void) {
    memset(&cfg, 0, sizeof(cfg));
    cfg.channel_value = 1 << 0;
    cfg.com_mode = RS485_MODE;
    cfg.com_tcp_mode = TCP_SRV_MODE;
    cfg.ctrl_mode = MANUAL_MODE;
    for (int i = 0; i < 8; i++) {
        cfg.poll_time[i] = 5;
    }

    cfg.ip[0] = 192;
    cfg.ip[1] = 168;
    cfg.ip[2] = 201;
    cfg.ip[3] = 211;
    cfg.port = 4196;

    cfg.dest_ip[0] = 192;
    cfg.dest_ip[1] = 168;
    cfg.dest_ip[2] = 201;
    cfg.dest_ip[3] = 13;
    cfg.dest_port = 5697;

    cfg.rs485_id = 1;
    cfg.rs485_buad = 9600;
    cfg.need_ota = 0;
    printf("reset local cfg\r\n");
    spi_write_cfg(&cfg);
}

uint16_t RELAY_GPIO_PIN[8] = {GPIO_PIN_0, GPIO_PIN_1, GPIO_PIN_2, GPIO_PIN_3,
                              GPIO_PIN_4, GPIO_PIN_5, GPIO_PIN_6, GPIO_PIN_7};

static int io_index = 0;
static int run_time = 0;

void TimerCallback(void const *argument) {
    // printf("auto ctrl running!\r\n");

    if (run_time == 0) {
        uint16_t io_pins = 0;
        for (int i = 0; i < 8; i++) {
            if (i != io_index) {
                io_pins |= RELAY_GPIO_PIN[i];
                cfg.channel_value &= ~(1 << i);
            } else {
                cfg.channel_value |= 1 << i;
            }
        }
        // printf("set io: %d, %d s\r\n", index, run_time);
        HAL_GPIO_WritePin(GPIOD, io_pins, GPIO_PIN_RESET);
        HAL_GPIO_WritePin(GPIOD, RELAY_GPIO_PIN[io_index], GPIO_PIN_SET);
    }

    if (++run_time == cfg.poll_time[io_index]) {
        run_time = 0;
        int i = 8;
        while (i-- > 0) {
            io_index = ++io_index % 8;
            if (cfg.poll_time[io_index] != 0) {
                break;
            }
        }
    }
}

uint16_t CRC16_Calculate(uint8_t *data, uint16_t length) {
    uint16_t crc = 0xFFFF;
    uint16_t i, j;

    for (i = 0; i < length; i++) {
        crc ^= data[i];
        for (j = 0; j < 8; j++) {
            if (crc & 0x0001) {
                crc = (crc >> 1) ^ 0xA001;
            } else {
                crc = crc >> 1;
            }
        }
    }
    return crc;
}

void print_local_cfg() {
    printf("=========curent cfg start=========\r\n");
    printf("communication model(0:RS485, 1:TCP) :%d \r\n", cfg.com_mode);
    printf("tcp mode(0:Server 1:Client) :%d \r\n", cfg.com_tcp_mode);
    printf("ctrl mode(0:command  1:auto) :%d \r\n", cfg.ctrl_mode);
    printf("channel value :%d \r\n", cfg.channel_value);
    printf("rs485 addr :%d \r\n", cfg.rs485_id);
    printf("rs485 buad :%d \r\n", cfg.rs485_buad);
    for (int i = 0; i < 8; i++) {
        printf("io [%d] sec :%d \r\n", i, cfg.poll_time[i]);
    }
    printf("local ip :%d.%d.%d.%d\r\n", cfg.ip[0], cfg.ip[1], cfg.ip[2],
           cfg.ip[3]);
    printf("local port :%d \r\n", cfg.port);
    printf("dest ip :%d.%d.%d.%d\r\n", cfg.dest_ip[0], cfg.dest_ip[1],
           cfg.dest_ip[2], cfg.dest_ip[3]);
    printf("dest port :%d \r\n", cfg.dest_port);
    printf("=========curent cfg end =========\r\n");
}

void load_local_cfg() {
    spi_read_cfg(&cfg);
    print_local_cfg();
    // restore_cfg();
    memcpy(&new_cfg, &cfg, sizeof(cfg));
    if (CRC16_Calculate((uint8_t *)&cfg, sizeof(dev_cfg_t) - 2) !=
        cfg.crc_valid) {
        restore_cfg();
    }
}

int ParseFrame(uint8_t *buffer, uint16_t length, ProtocolFrame_t *frame) {
    if (length < 11) {
        return -1;
    }

    if (buffer[0] != CMD_HEADER || buffer[1] != 0x55 ||
        buffer[2] != CMD_HEADER || buffer[3] != 0x55) {
        return -2;
    }

    memcpy(frame->cmdHeader, buffer, 4);

    frame->address = buffer[4];

    if (frame->address != cfg.rs485_id) {
        return -3;
    }

    frame->functionCode = (buffer[5] << 8) | buffer[6];

    frame->dataLength = (buffer[7] << 8) | buffer[8];

    if (length < (9 + frame->dataLength + 2)) {
        return -4;
    }

    memcpy(frame->data, &buffer[9], frame->dataLength);

    frame->checksum =
        (buffer[10 + frame->dataLength] << 8) | buffer[9 + frame->dataLength];

    uint16_t calculatedCRC = CRC16_Calculate(&buffer[4], 5 + frame->dataLength);

    if (calculatedCRC != frame->checksum) {
        printf("crc err:%x, %x\r\n", calculatedCRC, frame->checksum);
        return -5;
    }

    return 0;
}
extern osTimerId relayTimerHandle;
void set_io(uint8_t *cmd) {
    cfg.ctrl_mode = cmd[0];
    osTimerStop(relayTimerHandle);
    if (cmd[0] == AUTO_MODE) {
        // osTimerStop(relayTimerHandle);
        io_index = 0;
        run_time = 0;
        for (int i = 0; i < 8; i++) {
            cfg.poll_time[i] = cmd[i * 2 + 1] << 8 | cmd[i * 2 + 2];
        }
        osTimerStart(relayTimerHandle, 1000);
    } else if (cmd[0] == MANUAL_MODE) {
        for (int i = 0; i < 8; i++) {
            uint16_t val = cmd[i * 2 + 1] << 8 | cmd[i * 2 + 2];
            if (val == 0xFFFF) {
                cfg.channel_value |= 1 << i;
            } else {
                cfg.channel_value &= ~(1 << i);
            }
        }
        printf("cfg.channel_value = %d\r\n", cfg.channel_value);
        HAL_GPIO_WritePin(GPIOD, ~cfg.channel_value, GPIO_PIN_RESET);
        HAL_GPIO_WritePin(GPIOD, cfg.channel_value, GPIO_PIN_SET);
    }
}

int ProcessFrame(ProtocolFrame_t *frame, uint8_t *ack_buf, uint8_t *ack_len) {
    int ret = 0;
    uint8_t index = 7;
    uint16_t crc = 0;
    int i = 0;
    printf("parse cmd code = %04x->", frame->functionCode);
    switch (frame->functionCode) {
        case 0x0001:
            set_io(frame->data);
            ack_buf[index++] = 0x0;
            ack_buf[index++] = 0x1;
            ack_buf[index++] = 0x0;
            break;
        case 0x0002:
            ack_buf[index++] = 0x0;
            ack_buf[index++] = 0x11;
            ack_buf[index++] = cfg.ctrl_mode;
            for (i = 0; i < 8; i++) {
                ack_buf[index++] = cfg.poll_time[i] >> 8;
                ack_buf[index++] = cfg.poll_time[i] & 0xFF;
            }
            break;
        case 0x0003:
            ack_buf[index++] = 0x0;
            ack_buf[index++] = 0x1;
            ack_buf[index++] = cfg.channel_value;
            break;
        case 0x0101:
            ack_buf[index++] = 0x0;
            ack_buf[index++] = 0x6;
            ack_buf[index++] = 0x13;  // hardware version
            ack_buf[index++] = 0x56;  // software version
            ack_buf[index++] = 0x12;  // SN
            ack_buf[index++] = 0x34;  // SN
            ack_buf[index++] = 0x56;  // SN
            ack_buf[index++] = 0x78;  // SN
            break;
        case 0x0102:
            ack_buf[index++] = 0x0;
            ack_buf[index++] = 0x11;
            ack_buf[index++] = cfg.com_mode;
            ack_buf[index++] = cfg.com_tcp_mode;
            ack_buf[index++] = cfg.ip[0];
            ack_buf[index++] = cfg.ip[1];
            ack_buf[index++] = cfg.ip[2];
            ack_buf[index++] = cfg.ip[3];
            ack_buf[index++] = cfg.port >> 8;
            ack_buf[index++] = cfg.port & 0xFF;
            ack_buf[index++] = cfg.dest_ip[0];
            ack_buf[index++] = cfg.dest_ip[1];
            ack_buf[index++] = cfg.dest_ip[2];
            ack_buf[index++] = cfg.dest_ip[3];
            ack_buf[index++] = cfg.dest_port >> 8;
            ack_buf[index++] = cfg.dest_port & 0xFF;
            ack_buf[index++] = cfg.rs485_id;
            ack_buf[index++] = cfg.rs485_buad >> 24;
            ack_buf[index++] = cfg.rs485_buad >> 16;
            ack_buf[index++] = cfg.rs485_buad >> 8;
            ack_buf[index++] = cfg.rs485_buad & 0xFF;
            break;
        case 0x0103:
            cfg.com_mode = frame->data[0];
            cfg.com_tcp_mode = frame->data[1];
            new_cfg.ip[0] = frame->data[2];
            new_cfg.ip[1] = frame->data[3];
            new_cfg.ip[2] = frame->data[4];
            new_cfg.ip[3] = frame->data[5];
            new_cfg.port = frame->data[6] << 8 | frame->data[7];
            new_cfg.dest_ip[0] = frame->data[8];
            new_cfg.dest_ip[1] = frame->data[9];
            new_cfg.dest_ip[2] = frame->data[10];
            new_cfg.dest_ip[3] = frame->data[11];
            new_cfg.dest_port = frame->data[12] << 8 | frame->data[13];
            cfg.rs485_id = frame->data[14];
            new_cfg.rs485_buad = frame->data[15] << 24 | frame->data[16] << 16 |
                                 frame->data[17] << 8 | frame->data[18];
            ack_buf[index++] = 0x0;
            ack_buf[index++] = 0x1;
            ack_buf[index++] = 0x0;

            break;
        case 0x0104:
            ack_buf[index++] = 0x0;
            ack_buf[index++] = 0x1;
            ack_buf[index++] = 0x0;
            break;
        case 0x0105:
            restore_cfg();
            ack_buf[index++] = 0x0;
            ack_buf[index++] = 0x1;
            ack_buf[index++] = 0x0;
            break;
        case 0x0201:  // set ota flag
            cfg.need_ota = frame->data[0];
            ack_buf[index++] = 0x0;
            ack_buf[index++] = 0x1;
            ack_buf[index++] = 0x0;
            break;
        default:
            ret = -1;
            break;
    }
    crc = CRC16_Calculate(&ack_buf[4], index - 4);
    ack_buf[index++] = crc & 0xFF;
    ack_buf[index++] = crc >> 8;
    *ack_len = index;
    return ret;
}

static uint32_t last_call_time = 0;
void cmd_process_detail(uint8_t *localBuffer, uint16_t localLength,
                        dev_send_ack cb, int index) {
    uint8_t ack_buf[64] = {0};
    uint8_t ack_len = 0;
    int ret = 0;
    uint16_t crc = 0;

    ack_buf[0] = localBuffer[0];
    ack_buf[1] = localBuffer[1];
    ack_buf[2] = localBuffer[2];
    ack_buf[3] = localBuffer[3];
    // id
    ack_buf[4] = localBuffer[4];
    // func
    ack_buf[5] = localBuffer[5];
    ack_buf[6] = localBuffer[6];
    ack_len = 6;

    // filter less then 50ms
    uint32_t now = HAL_GetTick();
    if ((uint32_t)(now - last_call_time) < 50) {
		int funcCode = (localBuffer[5] << 8) | localBuffer[6];
        // 如果需要写入flash  需要50ms内应答设备忙
		if (funcCode == 0x0103 || funcCode == 0x0201) {
			ack_buf[ack_len++] |= 0x80;
			ack_buf[ack_len++] = 0x0;
			ack_buf[ack_len++] = 0x1;
			ack_buf[ack_len++] = DEVICE_BUSY;
			crc = CRC16_Calculate(&ack_buf[4], ack_len - 4);
			ack_buf[ack_len++] = crc & 0xFF;
			ack_buf[ack_len++] = crc >> 8;
			cb(ack_buf, ack_len, index);
			return;
		}
    }
    last_call_time = now;

    ProtocolFrame_t frame;
    ret = ParseFrame(localBuffer, localLength, &frame);
    if (ret == 0) {
        int r = ProcessFrame(&frame, ack_buf, &ack_len);
        if (r == 0) {
            cb(ack_buf, ack_len, index);
            if (new_cfg.rs485_buad != cfg.rs485_buad) {
                cfg.rs485_buad = new_cfg.rs485_buad;
                reset_uart();
            }
            if (new_cfg.ip[0] != cfg.ip[0] || new_cfg.ip[1] != cfg.ip[1] ||
                new_cfg.ip[2] != cfg.ip[2] || new_cfg.ip[3] != cfg.ip[3]) {
                cfg.ip[0] = new_cfg.ip[0];
                cfg.ip[1] = new_cfg.ip[1];
                cfg.ip[2] = new_cfg.ip[2];
                cfg.ip[3] = new_cfg.ip[3];
                reset_ip_and_restart_tcp();
            }

            if (new_cfg.dest_ip[0] != cfg.dest_ip[0] ||
                new_cfg.dest_ip[1] != cfg.dest_ip[1] ||
                new_cfg.dest_ip[2] != cfg.dest_ip[2] ||
                new_cfg.dest_ip[3] != cfg.dest_ip[3]) {
                cfg.dest_ip[0] = new_cfg.dest_ip[0];
                cfg.dest_ip[1] = new_cfg.dest_ip[1];
                cfg.dest_ip[2] = new_cfg.dest_ip[2];
                cfg.dest_ip[3] = new_cfg.dest_ip[3];
                reset_ip_and_restart_tcp();
            }

            if (new_cfg.port != cfg.port) {
                cfg.port = new_cfg.port;
                reset_ip_and_restart_tcp();
            }

            if (new_cfg.dest_port != cfg.dest_port) {
                cfg.dest_port = new_cfg.dest_port;
                reset_ip_and_restart_tcp();
            }

            if (frame.functionCode == 0x0103 || frame.functionCode == 0x0201) {
                spi_write_cfg(&cfg);
            }

        } else {
            ack_buf[ack_len++] |= 0x80;
            ack_buf[ack_len++] = 0x0;
            ack_buf[ack_len++] = 0x1;
            if (r == -1) ack_buf[ack_len++] = ILLEGAL_FUNC;
            crc = CRC16_Calculate(&ack_buf[4], ack_len - 4);
            ack_buf[ack_len++] = crc & 0xFF;
            ack_buf[ack_len++] = crc >> 8;
            cb(ack_buf, ack_len, index);
        }

        if (frame.functionCode == 0x0104) {
            osDelay(10);
            __set_FAULTMASK(1);
            NVIC_SystemReset();
        }
    } else {
        printf("parse error\r\n");
        ack_buf[ack_len++] |= 0x80;
        ack_buf[ack_len++] = 0x0;
        ack_buf[ack_len++] = 0x1;
        if (ret == -3) {
            ack_buf[ack_len++] = ILLEGAL_ADDRESS;
        } else if (ret == -4) {
            ack_buf[ack_len++] = ILLEGAL_DATA;
        } else if (ret == -5) {
            ack_buf[ack_len++] = CRC_ERR;
        } else {
            ack_buf[ack_len++] = UNKNOW_ERR;
        }
        crc = CRC16_Calculate(&ack_buf[4], ack_len - 4);
        ack_buf[ack_len++] = crc & 0xFF;
        ack_buf[ack_len++] = crc >> 8;
        cb(ack_buf, ack_len, index);
    }
}
